package com.abel.bigwater.data.jnrq

import com.abel.bigwater.data.BcdString
import io.netty.buffer.ByteBuf
import org.joda.time.DateTime
import java.net.InetAddress
import java.net.InetSocketAddress
import kotlin.experimental.and

/**
 * 3、修改 IP 地址，下行报文（服务器→集中器）
 * 说明：
 IP 地址中前四字节 P0H P1H P2H P3H 为服务器 IP，随后两个字节 X1X2H 为服务器端口
号， 高字节 X2 在后， YYH 位传输方式， 01 为 UDP,02 为 TCP， CSH 为 IP 地址校验位，
CSH =P0H+P1H+ P2H +P3H+X1H+X2H+YYH , 不计超出 FFH 的溢出值,
服务器 IP 地址解析， IP 地址 = P0.P1.P2.P3，端口号=X2X1H,
例如： IP 地址字节以此为 71H 38H 1eH 23H 5aH 1bH 02H 61H
IP 地址为 113.56.30.35 端口号 1b5Ah = 7002,传输协议 TCP
61H = (71H+38H +1eH+ 23H+ 5aH +1bH+ 02H)&&0xFF。
 服务器时间、上传时间周期/时刻、抄表周期/时刻集中器设备数据不解析。
 备用位置默认均为 00H
 */
class UpdateIpData : JnrqData() {

    val bak1 = 0

    val sampleSwitch = 0

    var ipStr: ByteArray? = null
    var ip: InetAddress? = null
        get() {
            field = InetAddress.getByAddress(ipStr)
            return field
        }
        set(value) {
            field = value
            ipStr = field?.address
        }

    var port: Int? = null

    var socketAddr: InetSocketAddress? = null
        get() {
            field = InetSocketAddress(ip, port!!)
            return field
        }
        set(value) {
            field = value
            ip = field!!.address
            port = field!!.port
        }

    var streamType: JnrqStreamType? = null

    var ipChecksum: Byte? = null

    val bak3 = byteArrayOf(0, 0, 0, 0)

    val bak4 = byteArrayOf(0, 0)

    val bak5 = byteArrayOf(0, 0)

    /** 服务器时间, 8个长度*/
    var serverTimeStr: BcdString? = null

    var serverTime: DateTime?
        get() = serverTimeStr?.toJnrqDateTime2()
        set(value) {
            serverTimeStr = BcdString.fromJnrqServerTime2(value!!)
        }

    /** 上传周期 */
    var uploadPeriodStr: ByteArray = byteArrayOf(0, 0, 6, 0)

    /** 抄表周期 */
    var readPeriodStr: ByteArray = byteArrayOf(0, 0xA, 0, 0)

    /** 上传时刻 */
    var uploadClockStr: ByteArray = byteArrayOf(0, 0, 5, 1)

    /** 抄表时刻 */
    var readClockStr: ByteArray = byteArrayOf(0, 0, 6, 1)

    var bak6: Short = 0

    var bak7: Short = 0


    /**
     * 计算数据长度，针对解析和打包
     */
    override fun computeDataLen(): Int {
        dataLen = 44
        return dataLen!!
    }

    /**
     * 从当前位置写入打包字节流
     */
    override fun fillBuffer(buf: ByteBuf) {
        buf.alloc().buffer().apply {
            writeByte(bak1)
            writeByte(sampleSwitch)

            assert(4 == ipStr?.size)

            writeBytes(ipStr)
            writeShortLE(port!!)
            writeByte(streamType!!.code)
            ipChecksum = computeIpChecksum()
            writeByte(ipChecksum!!.toInt())

            writeBytes(bak3).writeBytes(bak4).writeBytes(bak5)
            serverTime = DateTime.now()
            writeBytes(serverTimeStr!!.hexArray)

            writeBytes(uploadPeriodStr).writeBytes(readPeriodStr)
            writeBytes(uploadClockStr).writeBytes(readClockStr)

            writeByte(bak6.toInt()).writeByte(bak7.toInt())

            // transfer to buf
            buf.writeBytes(this)
        }
    }

    private fun computeIpChecksum(): Byte {
        return (ipStr!![0].toShort().and(0xFF)
                + ipStr!![1].toShort().and(0xFF)
                + ipStr!![2].toShort().and(0xFF)
                + ipStr!![3].toShort().and(0xFF)
                + port!!.and(0xFF).toShort()
                + port!!.shr(8).and(0xFF).toShort()
                + streamType!!.code).and(0xFF).toByte()
    }

    /**
     * 解析字节流，从buf当前位置开始，并且消费其中的字节。
     */
    override fun parseBuffer(buf: ByteBuf) {
        buf.readBytes(dataLen!!).apply {
            skipBytes(2)

            ipStr = ByteArray(4).also { readBytes(it) }
            port = readShortLE().toInt().and(0xFFFF)
            streamType = JnrqStreamType.findByCode(readByte().toInt().and(0xFF))
            ipChecksum = readByte()

            assert(ipChecksum == computeIpChecksum())

            serverTimeStr = BcdString.create(this, 8)

            uploadPeriodStr = ByteArray(4).also { readBytes(it) }
            readPeriodStr = ByteArray(4).also { readBytes(it) }
            uploadClockStr = ByteArray(4).also { readBytes(it) }
            readClockStr = ByteArray(4).also { readBytes(it) }

            skipBytes(2)
        }
    }
}

enum class JnrqStreamType(_code: Int) {
    UDP(1),
    TCP(2);

    val code: Int = _code

    companion object {
        fun findByCode(_code: Int) = JnrqStreamType.values().find { it.code == _code }
    }
}
